package com.lkd.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.base.Strings;
import com.lkd.common.VMSystem;
import com.lkd.config.TopicConfig;
import com.lkd.contract.SupplyChannel;
import com.lkd.contract.SupplyContract;
import com.lkd.contract.TaskCompleteContract;
import com.lkd.dao.TaskDao;
import com.lkd.emq.MqttProducer;
import com.lkd.entity.TaskDetailsEntity;
import com.lkd.entity.TaskEntity;
import com.lkd.exception.LogicException;
import com.lkd.feign.UserService;
import com.lkd.feign.VMService;
import com.lkd.http.vo.TaskDetailsViewModel;
import com.lkd.http.vo.TaskViewModel;
import com.lkd.service.TaskDetailsService;
import com.lkd.service.TaskService;
import com.lkd.vo.Pager;
import com.lkd.vo.UserVO;
import com.lkd.vo.VmVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
public class TaskServiceImpl extends ServiceImpl<TaskDao, TaskEntity> implements TaskService {

    @Autowired
    private VMService vmService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UserService userService;
    @Autowired
    private TaskDetailsService taskDetailsService;
    @Autowired
    private MqttProducer mqttProducer;

    @Override
    public Pager<TaskEntity> search(Long pageIndex, Long pageSize, String innerCode, Integer userId, String taskCode, Integer status, Boolean isRepair, String start, String end) {
        Page<TaskEntity> page = new Page<>(pageIndex, pageSize);
        LambdaQueryWrapper<TaskEntity> qw = new LambdaQueryWrapper<>();
        if (!Strings.isNullOrEmpty(innerCode)) {
            qw.eq(TaskEntity::getInnerCode, innerCode);
        }
        if (userId != null && userId > 0) {
            qw.eq(TaskEntity::getUserId, userId);
        }
        if (!Strings.isNullOrEmpty(taskCode)) {
            qw.like(TaskEntity::getTaskCode, taskCode);
        }
        if (status != null && status > 0) {
            qw.eq(TaskEntity::getTaskStatus, status);
        }
        if (isRepair != null) {
            if (isRepair) {
                qw.ne(TaskEntity::getProductTypeId, VMSystem.TASK_TYPE_SUPPLY);
            } else {
                qw.eq(TaskEntity::getProductTypeId, VMSystem.TASK_TYPE_SUPPLY);
            }
        }
        if (!Strings.isNullOrEmpty(start) && !Strings.isNullOrEmpty(end)) {
            qw
                    .ge(TaskEntity::getCreateTime, LocalDate.parse(start, DateTimeFormatter.ISO_LOCAL_DATE))
                    .le(TaskEntity::getCreateTime, LocalDate.parse(end, DateTimeFormatter.ISO_LOCAL_DATE));
        }
        //根据最后更新时间倒序排序
        qw.orderByDesc(TaskEntity::getUpdateTime);

        return Pager.build(this.page(page, qw));
    }

    /**
     * 运营工单和运维工单二合一
     *
     * @param taskVM 工单信息
     * @return
     */
    @Override
    public boolean creat(TaskViewModel taskVM) {
        //得到设备编号
        String innerCode = taskVM.getInnerCode();
        //得到工单类型
        int type = taskVM.getProductType();

        //1.检查前端传入的数据的设备状态
        checkStatus(innerCode, type);

        //1.1参数校验
        checkParam(taskVM);

        //1.2校验同一台设备下是否存在未完成的工单
        checkOrder(innerCode, type);

        //1.3校验工单状态，并向设备发送状态信息


        //2.售货机相关的信息，我们是需要通过feign调用VMService来得到的
        VmVO vmInfo = vmService.getVMInfo(taskVM.getInnerCode());

        //3.将数据存入工单表
        TaskEntity task = new TaskEntity();
        //查询设备才有的相关信息,并存入工单表
        //通过设备信息得到点位和编码
        task.setAddr(vmInfo.getNodeAddr());
        task.setInnerCode(vmInfo.getInnerCode());

        //4.从TaskViewModel对象中拿到数据，将数据存入工单表
        task.setUserId(taskVM.getUserId());
        task.setTaskCode(codeNum());
        task.setCreateType(taskVM.getCreateType());
        task.setAssignorId(taskVM.getAssignorId());
        task.setDesc(taskVM.getDesc());
        task.setProductTypeId(taskVM.getProductType());
        //查询执行人的name并存入
        UserVO user = userService.getUser(taskVM.getUserId());
        task.setUserName(user.getUserName());
        //保存
        save(task);

        //进行工单校验，如果是补货工单的话就走这个方法
        if (taskVM.getProductType() == VMSystem.TASK_TYPE_SUPPLY) {
            List<TaskDetailsViewModel> details = taskVM.getDetails();
            //如果补货为空就抛出异常
            if (StringUtils.isEmpty(details)) {
                throw new LogicException("补货详情未创建");
            }
            for (TaskDetailsViewModel detail : details) {
                TaskDetailsEntity taskDetailsEntity = new TaskDetailsEntity();
                //拷贝到工单详情表中
                BeanUtils.copyProperties(detail, taskDetailsEntity);
                taskDetailsEntity.setTaskId(task.getTaskId());
                taskDetailsService.save(taskDetailsEntity);
            }
        }
        return true;
    }

    /**
     * 接受工单
     *
     * @param id
     * @return
     */
    @Override
    public boolean accept(Long id) {
        TaskEntity task = this.getById(id);  //查询工单
        if (task.getTaskStatus() != VMSystem.TASK_STATUS_CREATE) {
            throw new LogicException("工单状态不是待处理");
        }
        task.setTaskStatus(VMSystem.TASK_STATUS_PROGRESS);//修改工单状态为进行
        return this.updateById(task);
    }

    /**
     * 完成工单 当我们完成工单的时候就要更新售货机的状态
     *
     * @param id 工单Id
     * @return
     */
    @Override
    public boolean completeTask(long id) {
        TaskEntity taskEntity = this.getById(id);
        if (taskEntity.getTaskStatus() == VMSystem.TASK_STATUS_FINISH || taskEntity.getTaskStatus() == VMSystem.TASK_STATUS_CANCEL) {
            throw new LogicException("工单已经结束");
        }
        taskEntity.setTaskStatus(VMSystem.TASK_STATUS_FINISH);//修改工单状态
        this.updateById(taskEntity);
        //根据工单类型修改售货机的状态,如果是投放和撤机工单就下放，发送消息
        if (taskEntity.getProductTypeId() == VMSystem.TASK_TYPE_DEPLOY || taskEntity.getProductTypeId() == VMSystem.TASK_TYPE_REVOKE) {
            delegate(taskEntity);
        }
        //如果是补货工单
        if (taskEntity.getProductTypeId() == VMSystem.TASK_TYPE_SUPPLY) {
            updateStock(taskEntity);
        }
        return true;
    }

    /**
     * 将运维工单封装并发送到设备微服务--更改库存
     *
     * @param taskEntity
     */
    private void updateStock(TaskEntity taskEntity) {
        //查询工单明细表
        LambdaQueryWrapper<TaskDetailsEntity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TaskDetailsEntity::getTaskId,taskEntity.getTaskId());
        List<TaskDetailsEntity> DetailsList = taskDetailsService.list(wrapper);
        //得到售货机编号，并将售货机编号存入实体类中
        SupplyContract supplyContract = new SupplyContract();
        supplyContract.setInnerCode(taskEntity.getInnerCode());

        List<SupplyChannel> supplyChannels = new ArrayList<>();
        for (TaskDetailsEntity taskDetailsEntity : DetailsList) {
            //获取货道编号，补货数量，存入货道表中
            SupplyChannel supplyChannel = new SupplyChannel();
            //补货数量
            supplyChannel.setCapacity(taskDetailsEntity.getExpectCapacity());
            //货道编号
            supplyChannel.setChannelId(taskDetailsEntity.getChannelCode());
            supplyChannels.add(supplyChannel);
        }
        //将数据存入实体类封装
        supplyContract.setSupplyData(supplyChannels);

        //发送信息
        try {
            mqttProducer.send(TopicConfig.VMS_SUPPLY_TOPIC,2,supplyContract);
        } catch (JsonProcessingException e) {
            log.error("发送工单协协议错误");
            throw new LogicException("发送工单协协议错误");
        }
    }

    /**
     * 将运维工单封装并发送到设备微服务--更改售货机状态
     *
     * @param taskEntity 订单对象
     */
    private void delegate(TaskEntity taskEntity) {
        //封装消息
        TaskCompleteContract contract = new TaskCompleteContract();
        contract.setInnerCode(taskEntity.getInnerCode());
        contract.setTaskType(taskEntity.getProductTypeId());
        //发送消息
        try {
            mqttProducer.send(TopicConfig.VMS_COMPLETED_TOPIC, 2, contract);
        } catch (JsonProcessingException e) {
            log.error("发送工单协协议错误");
            throw new LogicException("发送工单协协议错误");
        }
    }


    /**
     * 校验同一台设备下是否存在未完成的工单
     *
     * @param innerCode 设备编号
     * @param type      工单类型
     */
    private void checkOrder(String innerCode, int type) {
        LambdaQueryWrapper<TaskEntity> wrapper = new LambdaQueryWrapper<>();
        //查询该订单下面的同一台设备
        LambdaQueryWrapper<TaskEntity> wrapper1 = wrapper.select(TaskEntity::getTaskId).eq(TaskEntity::getInnerCode, innerCode);
        //根据工单类型查询
        LambdaQueryWrapper<TaskEntity> wrapper2 = wrapper1.eq(TaskEntity::getProductTypeId, type);
        //校验该工单是否是未完成
        LambdaQueryWrapper<TaskEntity> wrapper3 = wrapper2.eq(TaskEntity::getTaskStatus, VMSystem.TASK_STATUS_PROGRESS);
        //查询是否有未完成，如果有抛出异常
        TaskEntity task = getOne(wrapper3);
        if (task != null) {
            throw new LogicException("该设备还有未完成的工单");
        }

    }

    /**
     * 参数校验
     */
    private void checkParam(TaskViewModel taskVM) {
        if (StringUtils.isEmpty(taskVM.getInnerCode())) {
            throw new LogicException("该设备编号不存在");
        }
        if (StringUtils.isEmpty(taskVM.getProductType())) {
            throw new LogicException("请选择工  单类型");
        }
    }


    /**
     * 设备检测
     *
     * @param type 工单状态
     */
    private void checkStatus(String innerCode, int type) {
        //根据设备编号查询设备，得到设备信息
        VmVO vmInfo = vmService.getVMInfo(innerCode);
        if (vmInfo == null) {
            throw new LogicException("设备校验失败");
        }
        if (type == VMSystem.VM_STATUS_RUNNING && vmInfo.getStatus() == VMSystem.VM_STATUS_RUNNING) {
            throw new LogicException("该设备正在运营");
        }
        if (type == VMSystem.TASK_TYPE_SUPPLY && vmInfo.getStatus() != VMSystem.VM_STATUS_RUNNING) {
            throw new LogicException("该设备未运营");
        }
        if (type == VMSystem.TASK_TYPE_REVOKE && vmInfo.getStatus() != VMSystem.VM_STATUS_RUNNING) {
            throw new LogicException("该设备未运营");
        }

    }

    /**
     * 工单编号
     *
     * @return 格式为8位年月日+4位当日序号
     */
    private String codeNum() {
        //将得到当天的日期
        String dataNum = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        //存入redis的key取一个和业务相关的
        String key = "lkd.task.code" + dataNum;
        //如果redis中有数据直接取出来
        Object Key = redisTemplate.opsForValue().get(key);
        //如果没有就创建第一个编号
        if (Key == null) {
            redisTemplate.opsForValue().set(key, 0L, Duration.ofDays(1));//设置为一天更新
            return dataNum + "0001";
        }
        String code = dataNum + String.format("%04d", redisTemplate.opsForValue().increment(key, 1));

        return code;

    }
}